import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Scanner;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.UIManager;

public class SimulatorUI extends JFrame {

	Parser parser;
	JPanel codeNerror, regFile, Datapath;
	JTextArea errors, codeArea, Flow;
	JTextField inst;
	JTextField[] regs;
	MenuItem next, start, assemble;
	String code;
	String[] regNames = new String[] { "$zero", "$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3", "$t0", "$t1", "$t2", "$t3",
			"$t4", "$t5", "$t6", "$t7", "$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7", "$t8", "$t9", "$k0", "$k1",
			"$gp", "$sp", "$fp", "$ra" };
	int[] values;
	int nextcount = 0;

	public void close() {
		this.dispose();
	}

	public SimulatorUI() {
		setSize(1275, 678);
		setLocation(50, 20);
		setTitle("Java MIPS Simulator");
		setBackground(Color.GRAY);
		setResizable(false);
		setLayout(null);
		MenuBar menu = new MenuBar();
		Menu file = new Menu("File");
		menu.add(file);
		MenuItem newSim = new MenuItem("New");
		file.add(newSim);
		newSim.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				new SimulatorUI();
				close();
			}
		});

		MenuItem save = new MenuItem("Save");
		file.add(save);
		save.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				try {
					String fname = JOptionPane.showInputDialog("please enter file name");
					String text = codeArea.getText();
					String fpath = System.getProperty("user.home");
					File file = new File(fpath + "/Desktop", fname + ".txt");
					Writer output = new BufferedWriter(new FileWriter(file));
					output.write(text);
					output.close();
					if (fname != null || !fname.equals("") || !fname.equals("null"))
						JOptionPane.showMessageDialog(null, "File is now saved on the Desktop!");
				} catch (Exception e) {
					JOptionPane.showMessageDialog(null, "There is a problem saving file!");
				}
			}
		});

		MenuItem importfile = new MenuItem("import file");
		file.add(importfile);
		importfile.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				final File importedfile;
				final JFileChooser fc = new JFileChooser();
				int returnVal = fc.showOpenDialog(SimulatorUI.this);
				if (returnVal == JFileChooser.APPROVE_OPTION) {
					importedfile = fc.getSelectedFile();
					String s = "";
					try {
						Scanner sc = new Scanner(importedfile);
						while (sc.hasNext()) {
							s += sc.nextLine();
							if (sc.hasNext())
								s += "\n";
						}
					} catch (FileNotFoundException e) {
						JOptionPane.showMessageDialog(null, "File is not found!");
					} catch (Exception e) {
					} finally {
						codeArea.setText(s);
					}
				}
			}
		});
		MenuItem close = new MenuItem("Close");
		file.add(close);
		close.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				System.exit(0);
			}
		});
		setMenuBar(menu);
		Menu action = new Menu("Action");
		menu.add(action);
		start = new MenuItem("Start");
		action.add(start);
		start.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				parser.instructionMemo.InstructionMemory_Run();
				updateData();
				updateRegs();

				errors.setEnabled(false);
				codeArea.setEnabled(false);
				next.setEnabled(true);
				start.setEnabled(false);
				nextcount++;
			}
		});
		assemble = new MenuItem("Assemble");
		action.add(assemble);
		assemble.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				String errmsg = "";
				code = codeArea.getText();
				Registers regsFile = new Registers();
				for (int i = 0; i < 32; i++)
					try {
						regsFile.write(regNames[i], Integer.parseInt(regs[i].getText()));
					} catch (NumberFormatException e1) {
						errmsg += "\n" + regNames[i] + "'s value should be an integer.";
						break;
					}
				parser = new Parser(code, regsFile);
				int comp = parser.compile();
				if (comp == 0) {
					start.setEnabled(true);
					errmsg += "\n" + "Code assembled successfully.";
					errors.setForeground(new Color(0, 255, 0));
					errors.setText(errmsg);
					next.setEnabled(true);
					regFile.setEnabled(false);
					parser.Run();
				} else {
					System.out.println(comp);
					errors.setForeground(new Color(255, 0, 0));
					errors.setText("error at line " + comp);
				}
				validate();
				errors.updateUI();
			}
		});
		start.setEnabled(false);
		next = new MenuItem("Next");
		action.add(next);

		next.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				parser.instructionMemo.InstructionMemory_Run();
				updateData();
				updateRegs();
			}
		});
		next.setEnabled(false);

		setMenuBar(menu);
		codeNerror();
		generateRegFile(new int[32], true);
		add(regFile);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setVisible(true);
		Datapath();
	}

	public void codeNerror() {
		codeNerror = new JPanel();
		codeNerror.setSize(500, 675);
		codeNerror.setLocation(0, 25);
		codeNerror.setBackground(Color.GRAY);
		codeNerror.setLayout(null);
		JLabel tmp = new JLabel("Code", JLabel.LEFT);
		tmp.setSize(475, 25);
		tmp.setLocation(30, 0);
		codeNerror.add(tmp);
		codeArea = new JTextArea();
		codeArea.setSize(450, 400);
		codeArea.setFont(new Font("", 1, 13));
		codeArea.setLineWrap(true);
		JScrollPane scroll = new JScrollPane(codeArea);
		scroll.setSize(450, 470);
		scroll.setLocation(25, 25);
		scroll.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
		codeNerror.add(scroll);
		tmp = new JLabel("Console", JLabel.LEFT);
		tmp.setSize(475, 25);
		tmp.setLocation(30, 495);
		codeNerror.add(tmp);
		errors = new JTextArea();
		errors.setEditable(false);
		errors.setFont(new Font("", 1, 13));
		errors.setLineWrap(true);
		errors.setSize(450, 100);
		scroll = new JScrollPane(errors);
		scroll.setSize(450, 100);
		scroll.setLocation(25, 520);
		scroll.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
		codeNerror.add(scroll);
		add(codeNerror);
	}

	public void generateRegFile(int[] values, boolean editable) {
		regFile = new JPanel();
		regs = new JTextField[32];
		regFile.setSize(275, 675);
		regFile.setLocation(500, 25);
		regFile.setBackground(Color.GRAY);
		regFile.setLayout(null);
		JLabel tmp = new JLabel("Registers", JLabel.CENTER);
		tmp.setSize(275, 25);
		tmp.setLocation(0, 0);
		regFile.add(tmp);
		JPanel grid = new JPanel();
		grid.setLocation(0, 25);
		grid.setSize(275, 600);
		grid.setLayout(new GridLayout(33, 3));
		grid.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
		tmp = new JLabel("Name", JLabel.CENTER);
		tmp.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1));
		grid.add(tmp);
		tmp = new JLabel("Binary", JLabel.CENTER);
		tmp.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1));
		grid.add(tmp);
		tmp = new JLabel("Value", JLabel.CENTER);
		tmp.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1));
		grid.add(tmp);
		tmp = new JLabel(regNames[0], JLabel.CENTER);
		tmp.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1));
		grid.add(tmp);
		tmp = new JLabel("00000", JLabel.CENTER);
		tmp.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1));
		grid.add(tmp);
		for (int i = 1; i < 32; i++) {
			regs[0] = new JTextField("" + values[i]);
		}
		regs[0].setEditable(false);
		regs[0].setBorder(BorderFactory.createLineBorder(Color.GRAY, 1));
		grid.add(regs[0]);
		for (int i = 1; i < 32; i++) {
			tmp = new JLabel(regNames[i], JLabel.CENTER);
			tmp.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1));
			grid.add(tmp);
			tmp = new JLabel(toBin(i), JLabel.CENTER);
			tmp.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1));
			grid.add(tmp);
			regs[i] = new JTextField(values[i] + "");
			regs[i].setEditable(editable);
			regs[i].setBorder(BorderFactory.createLineBorder(Color.GRAY, 1));
			grid.add(regs[i]);
		}
		regFile.add(grid);
		regFile.updateUI();
	}

	public void Datapath() {
		Datapath = new JPanel();
		Datapath.setSize(500, 675);
		Datapath.setLocation(775, 25);
		Datapath.setBackground(Color.GRAY);
		Datapath.setLayout(null);

		JLabel tmp = new JLabel("Datapath", JLabel.CENTER);
		tmp.setSize(400, 25);
		tmp.setLocation(0, 0);
		Datapath.add(tmp);

		tmp = new JLabel("Instruction", JLabel.LEFT);
		tmp.setSize(400, 25);
		tmp.setLocation(30, 15);
		Datapath.add(tmp);

		inst = new JTextField();
		inst.setEditable(false);
		inst.setFont(new Font("", 1, 13));
		inst.setSize(400, 20);
		inst.setLocation(45, 40);
		inst.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
		Datapath.add(inst);

		tmp = new JLabel("Flow", JLabel.LEFT);
		tmp.setSize(400, 25);
		tmp.setLocation(30, 350);
		Datapath.add(tmp);

		Flow = new JTextArea();
		Flow.setEditable(false);
		Flow.setFont(new Font("", 1, 13));
		Flow.setLineWrap(true);
		Flow.setLocation(45, 375);
		Flow.setSize(400, 200);
		Flow.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
		Datapath.add(Flow);
		add(Datapath);
	}

	public String toBin(int x) {
		String s = "";
		while (x > 0) {
			int o = x % 2;
			s = o + s;
			x = x / 2;
		}
		while (s.length() < 5) {
			s = "0" + s;
		}
		return s;
	}

	public void updateRegs() {
		for (int i = 0; i < 32; i++)
			regs[i].setText(parser.RegisterFile.read(regNames[i]) + "");
	}

	public void updateData() {
		String instruction = "";
		String info = "";

		instruction = parser.instructionMemo.getCurrentInstruction();
		inst.setText(instruction);
		inst.updateUI();
		if (instruction.toLowerCase().equals("hlt") || instruction.toLowerCase().equals("halt") || instruction.toLowerCase().equals("end")) {
			next.setEnabled(false);
			JOptionPane.showMessageDialog(null, "the program has finished!");
		}

		info = parser.instructionMemo.getCurrentInstructionInfo();
		Flow.setText(info);
		Flow.updateUI();
	}

	public static void main(String[] args) {
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (Exception e) {
		}
		new SimulatorUI();
	}

}